package com.example.springlearning;

import com.example.springlearning.to.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @auth 罗俊华
 * @Date 2022/8/3 9:52 下午
 */

public class OrderService {

    private final Map<Long, BatchOrderTo> skuIdMappingOrderCountList = new ConcurrentHashMap<>();

    private final BlockingQueue<BatchOrderTo> subtractStockSkuIdQueue;

    private final AtomicInteger idWorker = new AtomicInteger(0);

    public OrderService(BlockingQueue<BatchOrderTo> subtractStockSkuIdQueue) {
        this.subtractStockSkuIdQueue = subtractStockSkuIdQueue;
    }


    public SubtractStockResult buy(long skuId, int count, int delay, TimeUnit timeUnit) throws InterruptedException {

        IBatchOrderTo batchOrderTo = skuIdMappingOrderCountList.computeIfAbsent(skuId, skuIdKey -> {

            BatchOrderTo result = new BatchOrderTo(skuIdKey, delay, timeUnit);

            try {
                subtractStockSkuIdQueue.put(result);
            } catch (InterruptedException e) {
                e.printStackTrace();
                throw new RuntimeException("队列已满，无法插入：", e);
            }
            return result;
        });

        int orderId = idWorker.getAndIncrement();

        OrderItemTo orderItemTo = new OrderItemTo(orderId, count);

        batchOrderTo.addCount(orderItemTo);

        return new SubtractStockResultProxy(batchOrderTo, orderItemTo);
    }


    /**
     * 批量扣减库存成功
     */
    public IBatchOrderTo batchSubtractStockSuccess(long skuId) {
        IBatchOrderTo IBatchOrderTo = skuIdMappingOrderCountList.remove(skuId);
        if (IBatchOrderTo == null) {
            return new EmptyBatchOrderTo(skuId);
        }
        IBatchOrderTo.batchSubtractStockSuccess();
        return IBatchOrderTo;
    }

    public IBatchOrderTo batchSubtractStockFail(long skuId) {
        IBatchOrderTo IBatchOrderTo = skuIdMappingOrderCountList.remove(skuId);
        if (IBatchOrderTo == null) {
            return new EmptyBatchOrderTo(skuId);
        }
        IBatchOrderTo.batchSubtractStockFail();
        return IBatchOrderTo;
    }

    /**
     * 贪婪的扣库存
     *
     * @return
     */
    public SubtractStockSummary greedySubtractStock(long skuId, AtomicInteger stock) {

        IBatchOrderTo batchOrderTo = skuIdMappingOrderCountList.remove(skuId);

        if (batchOrderTo == null) {

            return new EmptyBatchOrderTo(skuId);
        }

        boolean updateOrderStatusSuccess = batchOrderTo.splitSubtractOrder();

        if (!updateOrderStatusSuccess) {
            return new EmptyBatchOrderTo(skuId);
        }

        try {
            PriorityQueue<OrderItemTo> orderItemTos = new PriorityQueue<>((OrderItemTo o1, OrderItemTo o2) -> {
                return o1.getStock() - o2.getStock();
            });
            orderItemTos.addAll(batchOrderTo.getOrderIdSkuCountList());

//        要购买最多库存的订单


            List<OrderItemTo> failOrderItemList = new ArrayList<>();
            // 扣库存成功的订单数量
            List<OrderItemTo> successOrderItemList = new ArrayList<>();
            for (OrderItemTo maxStockOrder = orderItemTos.poll(); maxStockOrder != null; maxStockOrder = orderItemTos.poll()) {
                int newestStock = stock.get();
                if (maxStockOrder.getStock() > newestStock) {
//                  库存不够
                    failOrderItemList.add(maxStockOrder);
                } else {
                    if (stock.compareAndSet(newestStock, newestStock - maxStockOrder.getStock())) {
//                      库存够，并且已经扣减成功
                        maxStockOrder.updateOrderStockStatusToSuccess();
                        successOrderItemList.add(maxStockOrder);
                    } else {
//                    库存够，但是扣减失败
                        failOrderItemList.add(maxStockOrder);
                    }
                }
            }
            return new SplitBatchOrderStockTo(failOrderItemList, successOrderItemList);
        } finally {
            batchOrderTo.subtractStockFinish();
        }


    }


}
